package com.freedom.start.config;

import com.freedom.start.shiro.CustomFilter;
import com.freedom.start.shiro.CustomModularRealmAuthenticator;
import com.freedom.start.shiro.CustomRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.Authorizer;
import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.*;

// import org.apache.shiro.spring.config.web.autoconfigure.ShiroWebAutoConfiguration;

/**
 * @Author: zhenggang.liu
 * @Date: 2019/8/9 9:54
 */


@Configuration
public class ShiroConfig {


    @Bean("sessionManager")
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // sessionManager.setGlobalSessionTimeout(100000);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionIdCookieEnabled(true);
        sessionManager.setSessionDAO(sessionDAO());
        return sessionManager;
    }

    /**
     * 使用shiro自带的session管理，适用于简单的应用
     *
     * @return
     */
    @Bean
    public SessionDAO sessionDAO() {
        //使用默认的MemorySessionDAO
        return new MemorySessionDAO();
    }

    /**
     * 方法名必须为 shiroFilterFactoryBean，或者指定bean的名字为shiroFilterFactoryBean，因为 ShiroWebAutoConfiguration中会检测是否有此bean
     *
     * @param securityManager
     * @return
     */
    // @Bean("shiroFilter")
    @Bean("shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setSuccessUrl("/index");
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorize");

        Map<String, Filter> filters = new HashMap<>(16);
        filters.put("authc", new CustomFilter());
        shiroFilterFactoryBean.setFilters(filters);

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        // filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/druid/**", "anon");
        filterChainDefinitionMap.put("/remember", "user");
        // filterChainDefinitionMap.put("/front/**", "anon");
        // filterChainDefinitionMap.put("/api/**", "anon");
        filterChainDefinitionMap.put("/logout", "logout");
        //忽略过滤activiti资源
        filterChainDefinitionMap.put("/modeler.html", "anon");
        filterChainDefinitionMap.put("/service/**", "anon");
        filterChainDefinitionMap.put("/diagram-viewer/**", "anon");
        filterChainDefinitionMap.put("/editor-app/**", "anon");

        filterChainDefinitionMap.put("/act/**", "anon");
        //主要这行代码必须放在所有权限设置的最后，不然会导致所有 url 都被拦截 剩余的都需要认证
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;

    }

    /**
     * 防止shiro的注解不起作用
     * 强制使用cglib，防止重复代理和可能引起代理出错的问题
     * https://zhuanlan.zhihu.com/p/29161098
     *
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setUsePrefix(true);
        // proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }

    /**
     * 注意：返回值类型必须是 @SessionsSecurityManager
     *
     * @return
     */
    @Bean("securityManager")
    public SessionsSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        securityManager.setAuthenticator(customModularRealmAuthenticator());
        // securityManager.setRealm(customRealm());
        List<Realm> realms = new LinkedList<>();
        realms.add(customRealm());
        securityManager.setRealms(realms);
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /**
     * 多个realm的情况下认证失败时，总是将抛出的异常转换为新的@{@link org.apache.shiro.authc.AuthenticationException}异常的问题
     *
     * @return
     */
    @Bean
    public CustomModularRealmAuthenticator customModularRealmAuthenticator() {
        return new CustomModularRealmAuthenticator();
    }

    @Bean("customRealm")
    public CustomRealm customRealm() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");
        hashedCredentialsMatcher.setHashIterations(1024);
        CustomRealm customRealm = new CustomRealm();
        customRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return customRealm;
    }

    /**
     * 解决
     * org.apache.shiro.spring.boot.autoconfigure.ShiroAnnotationProcessorAutoConfiguration
     * required a bean named 'authorizer' that could not be found
     *
     * @return
     */
    @Bean
    public Authorizer authorizer() {
        return new ModularRealmAuthorizer();
    }


}
